/*-------------------------------------------------------------------------
 * oracle_compat.c
 *	Oracle 兼容函数。
 *
 * Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 *	作者：Edmund Mergl <E.Mergl@bawue.de>
 *	多字节增强：Tatsuo Ishii <ishii@postgresql.org>
 *
 *
 * IDENTIFICATION
 *	src/backend/utils/adt/oracle_compat.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "common/int.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/formatting.h"
#include "utils/memutils.h"


static text *fc_dotrim(const char *fc_string, int fc_stringlen,
					const char *fc_set, int fc_setlen,
					bool fc_doltrim, bool fc_dortrim);
static bytea *dobyteatrim(bytea *string, bytea *set,
						  bool doltrim, bool dortrim);


/********************************************************************
 *
 * lower
 *
 * 语法：
 *
 *	 text lower(text string)
 *
 * 目的：
 *
 *	 返回字符串，所有字母均转为小写。
 *
 ********************************************************************/

Datum lower(PG_FUNCTION_ARGS)
{
	text	   *fc_in_string = PG_GETARG_TEXT_PP(0);
	char	   *fc_out_string;
	text	   *fc_result;

	fc_out_string = str_tolower(VARDATA_ANY(fc_in_string),
							 VARSIZE_ANY_EXHDR(fc_in_string),
							 PG_GET_COLLATION());
	fc_result = cstring_to_text(fc_out_string);
	pfree(fc_out_string);

	PG_RETURN_TEXT_P(fc_result);
}


/********************************************************************
 *
 * upper
 *
 * 语法：
 *
 *	 text upper(text string)
 *
 * 目的：
 *
 *	 返回字符串，所有字母都强制转换为大写。
 *
 ********************************************************************/

Datum upper(PG_FUNCTION_ARGS)
{
	text	   *fc_in_string = PG_GETARG_TEXT_PP(0);
	char	   *fc_out_string;
	text	   *fc_result;

	fc_out_string = str_toupper(VARDATA_ANY(fc_in_string),
							 VARSIZE_ANY_EXHDR(fc_in_string),
							 PG_GET_COLLATION());
	fc_result = cstring_to_text(fc_out_string);
	pfree(fc_out_string);

	PG_RETURN_TEXT_P(fc_result);
}


/********************************************************************
 *
 * initcap
 *
 * 语法：
 *
 *	 text initcap(text string)
 *
 * 目的：
 *
 *	 返回字符串，每个单词的首字母大写，所有
 *	 其他字母小写。一个单词被定义为一个
 *	 字母数字字符的序列，由非字母数字字符分隔。
 *
 ********************************************************************/

Datum initcap(PG_FUNCTION_ARGS)
{
	text	   *fc_in_string = PG_GETARG_TEXT_PP(0);
	char	   *fc_out_string;
	text	   *fc_result;

	fc_out_string = str_initcap(VARDATA_ANY(fc_in_string),
							 VARSIZE_ANY_EXHDR(fc_in_string),
							 PG_GET_COLLATION());
	fc_result = cstring_to_text(fc_out_string);
	pfree(fc_out_string);

	PG_RETURN_TEXT_P(fc_result);
}


/********************************************************************
 *
 * lpad
 *
 * 语法：
 *
 *	 text lpad(text string1, int4 len, text string2)
 *
 * 目的：
 *
 *	 返回 string1，左侧填充至长度 len，填充字符为
 *	 string2 中的字符序列。如果 len 小于 string1 的长度，
 *	 则在右侧截断至 len。
 *
 ********************************************************************/

Datum lpad(PG_FUNCTION_ARGS)
{
	text	   *fc_string1 = PG_GETARG_TEXT_PP(0);
	int32		fc_len = PG_GETARG_INT32(1);
	text	   *fc_string2 = PG_GETARG_TEXT_PP(2);
	text	   *fc_ret;
	char	   *fc_ptr1,
			   *fc_ptr2,
			   *fc_ptr2start,
			   *fc_ptr2end,
			   *fc_ptr_ret;
	int			fc_m,
				fc_s1len,
				fc_s2len;
	int			fc_bytelen;

	/* 负长度默默被视为零 */
	if (fc_len < 0)
		fc_len = 0;

	fc_s1len = VARSIZE_ANY_EXHDR(fc_string1);
	if (fc_s1len < 0)
		fc_s1len = 0;				/* 不应该发生 */

	fc_s2len = VARSIZE_ANY_EXHDR(fc_string2);
	if (fc_s2len < 0)
		fc_s2len = 0;				/* 不应该发生 */

	fc_s1len = pg_mbstrlen_with_len(VARDATA_ANY(fc_string1), fc_s1len);

	if (fc_s1len > fc_len)
		fc_s1len = fc_len;			/* 将字符串1截断为len个字符 */

	if (fc_s2len <= 0)
		fc_len = fc_s1len;			/* 没有填充内容，因此不进行填充 */

	/* 计算最坏的输出长度 */
	if (unlikely(pg_mul_s32_overflow(pg_database_encoding_max_length(), fc_len,
									 &fc_bytelen)) ||
		unlikely(pg_add_s32_overflow(fc_bytelen, VARHDRSZ, &fc_bytelen)) ||
		unlikely(!AllocSizeIsValid(fc_bytelen)))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("requested length too large")));

	fc_ret = (text *) palloc(fc_bytelen);

	fc_m = fc_len - fc_s1len;

	fc_ptr2 = fc_ptr2start = VARDATA_ANY(fc_string2);
	fc_ptr2end = fc_ptr2 + fc_s2len;
	fc_ptr_ret = VARDATA(fc_ret);

	while (fc_m--)
	{
		int			fc_mlen = pg_mblen(fc_ptr2);

		memcpy(fc_ptr_ret, fc_ptr2, fc_mlen);
		fc_ptr_ret += fc_mlen;
		fc_ptr2 += fc_mlen;
		if (fc_ptr2 == fc_ptr2end)	/* 在s2的末尾循环 */
			fc_ptr2 = fc_ptr2start;
	}

	fc_ptr1 = VARDATA_ANY(fc_string1);

	while (fc_s1len--)
	{
		int			fc_mlen = pg_mblen(fc_ptr1);

		memcpy(fc_ptr_ret, fc_ptr1, fc_mlen);
		fc_ptr_ret += fc_mlen;
		fc_ptr1 += fc_mlen;
	}

	SET_VARSIZE(fc_ret, fc_ptr_ret - (char *) fc_ret);

	PG_RETURN_TEXT_P(fc_ret);
}


/********************************************************************
 *
 * rpad
 *
 * 语法：
 *
 *	 text rpad(text string1, int4 len, text string2)
 *
 * 目的：
 *
 *	 返回字符串1，右侧填充至长度len，填充字符为字符串2中的字符序列。 
 *	 如果len小于字符串1的长度，则在右侧截断至len。
 *
 ********************************************************************/

Datum rpad(PG_FUNCTION_ARGS)
{
	text	   *fc_string1 = PG_GETARG_TEXT_PP(0);
	int32		fc_len = PG_GETARG_INT32(1);
	text	   *fc_string2 = PG_GETARG_TEXT_PP(2);
	text	   *fc_ret;
	char	   *fc_ptr1,
			   *fc_ptr2,
			   *fc_ptr2start,
			   *fc_ptr2end,
			   *fc_ptr_ret;
	int			fc_m,
				fc_s1len,
				fc_s2len;
	int			fc_bytelen;

	/* 负长度默默被视为零 */
	if (fc_len < 0)
		fc_len = 0;

	fc_s1len = VARSIZE_ANY_EXHDR(fc_string1);
	if (fc_s1len < 0)
		fc_s1len = 0;				/* 不应该发生 */

	fc_s2len = VARSIZE_ANY_EXHDR(fc_string2);
	if (fc_s2len < 0)
		fc_s2len = 0;				/* 不应该发生 */

	fc_s1len = pg_mbstrlen_with_len(VARDATA_ANY(fc_string1), fc_s1len);

	if (fc_s1len > fc_len)
		fc_s1len = fc_len;			/* 将字符串1截断为len个字符 */

	if (fc_s2len <= 0)
		fc_len = fc_s1len;			/* 没有填充内容，因此不进行填充 */

	/* 计算最坏的输出长度 */
	if (unlikely(pg_mul_s32_overflow(pg_database_encoding_max_length(), fc_len,
									 &fc_bytelen)) ||
		unlikely(pg_add_s32_overflow(fc_bytelen, VARHDRSZ, &fc_bytelen)) ||
		unlikely(!AllocSizeIsValid(fc_bytelen)))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("requested length too large")));

	fc_ret = (text *) palloc(fc_bytelen);

	fc_m = fc_len - fc_s1len;

	fc_ptr1 = VARDATA_ANY(fc_string1);
	fc_ptr_ret = VARDATA(fc_ret);

	while (fc_s1len--)
	{
		int			fc_mlen = pg_mblen(fc_ptr1);

		memcpy(fc_ptr_ret, fc_ptr1, fc_mlen);
		fc_ptr_ret += fc_mlen;
		fc_ptr1 += fc_mlen;
	}

	fc_ptr2 = fc_ptr2start = VARDATA_ANY(fc_string2);
	fc_ptr2end = fc_ptr2 + fc_s2len;

	while (fc_m--)
	{
		int			fc_mlen = pg_mblen(fc_ptr2);

		memcpy(fc_ptr_ret, fc_ptr2, fc_mlen);
		fc_ptr_ret += fc_mlen;
		fc_ptr2 += fc_mlen;
		if (fc_ptr2 == fc_ptr2end)	/* 在s2的末尾循环 */
			fc_ptr2 = fc_ptr2start;
	}

	SET_VARSIZE(fc_ret, fc_ptr_ret - (char *) fc_ret);

	PG_RETURN_TEXT_P(fc_ret);
}


/********************************************************************
 *
 * btrim
 *
 * 语法：
 *
 *	 text btrim(text string, text set)
 *
 * 目的：
 *
 *	 返回从前面和后面去掉字符的字符串，直到遇到第一个不在集合中的字符。
 *
 ********************************************************************/

Datum btrim(PG_FUNCTION_ARGS)
{
	text	   *fc_string = PG_GETARG_TEXT_PP(0);
	text	   *fc_set = PG_GETARG_TEXT_PP(1);
	text	   *fc_ret;

	fc_ret = fc_dotrim(VARDATA_ANY(fc_string), VARSIZE_ANY_EXHDR(fc_string),
				 VARDATA_ANY(fc_set), VARSIZE_ANY_EXHDR(fc_set),
				 true, true);

	PG_RETURN_TEXT_P(fc_ret);
}

/********************************************************************
 *
 * btrim1 --- btrim，设置固定为' '
 *
 ********************************************************************/

Datum btrim1(PG_FUNCTION_ARGS)
{
	text	   *fc_string = PG_GETARG_TEXT_PP(0);
	text	   *fc_ret;

	fc_ret = fc_dotrim(VARDATA_ANY(fc_string), VARSIZE_ANY_EXHDR(fc_string),
				 " ", 1,
				 true, true);

	PG_RETURN_TEXT_P(fc_ret);
}

/*
 * btrim、ltrim、rtrim的通用实现
 */
static text * fc_dotrim(const char *fc_string, int fc_stringlen,
	   const char *fc_set, int fc_setlen,
	   bool fc_doltrim, bool fc_dortrim)
{
	int			fc_i;

	/* 如果字符串或集合为空，则无需操作 */
	if (fc_stringlen > 0 && fc_setlen > 0)
	{
		if (pg_database_encoding_max_length() > 1)
		{
			/*
			 * 在多字节编码的情况下，构建指向字符起始位置的指针数组，以避免在内部循环中不必要的检查。
			 */
			const char **fc_stringchars;
			const char **fc_setchars;
			int		   *fc_stringmblen;
			int		   *fc_setmblen;
			int			fc_stringnchars;
			int			fc_setnchars;
			int			fc_resultndx;
			int			fc_resultnchars;
			const char *fc_p;
			int			fc_len;
			int			fc_mblen;
			const char *fc_str_pos;
			int			fc_str_len;

			fc_stringchars = (const char **) palloc(fc_stringlen * sizeof(char *));
			fc_stringmblen = (int *) palloc(fc_stringlen * sizeof(int));
			fc_stringnchars = 0;
			fc_p = fc_string;
			fc_len = fc_stringlen;
			while (fc_len > 0)
			{
				fc_stringchars[fc_stringnchars] = fc_p;
				fc_stringmblen[fc_stringnchars] = fc_mblen = pg_mblen(fc_p);
				fc_stringnchars++;
				fc_p += fc_mblen;
				fc_len -= fc_mblen;
			}

			fc_setchars = (const char **) palloc(fc_setlen * sizeof(char *));
			fc_setmblen = (int *) palloc(fc_setlen * sizeof(int));
			fc_setnchars = 0;
			fc_p = fc_set;
			fc_len = fc_setlen;
			while (fc_len > 0)
			{
				fc_setchars[fc_setnchars] = fc_p;
				fc_setmblen[fc_setnchars] = fc_mblen = pg_mblen(fc_p);
				fc_setnchars++;
				fc_p += fc_mblen;
				fc_len -= fc_mblen;
			}

			fc_resultndx = 0;		/* 在stringchars[]中的索引 */
			fc_resultnchars = fc_stringnchars;

			if (fc_doltrim)
			{
				while (fc_resultnchars > 0)
				{
					fc_str_pos = fc_stringchars[fc_resultndx];
					fc_str_len = fc_stringmblen[fc_resultndx];
					for (fc_i = 0; fc_i < fc_setnchars; fc_i++)
					{
						if (fc_str_len == fc_setmblen[fc_i] &&
							memcmp(fc_str_pos, fc_setchars[fc_i], fc_str_len) == 0)
							break;
					}
					if (fc_i >= fc_setnchars)
						break;	/* 此处无匹配 */
					fc_string += fc_str_len;
					fc_stringlen -= fc_str_len;
					fc_resultndx++;
					fc_resultnchars--;
				}
			}

			if (fc_dortrim)
			{
				while (fc_resultnchars > 0)
				{
					fc_str_pos = fc_stringchars[fc_resultndx + fc_resultnchars - 1];
					fc_str_len = fc_stringmblen[fc_resultndx + fc_resultnchars - 1];
					for (fc_i = 0; fc_i < fc_setnchars; fc_i++)
					{
						if (fc_str_len == fc_setmblen[fc_i] &&
							memcmp(fc_str_pos, fc_setchars[fc_i], fc_str_len) == 0)
							break;
					}
					if (fc_i >= fc_setnchars)
						break;	/* 此处无匹配 */
					fc_stringlen -= fc_str_len;
					fc_resultnchars--;
				}
			}

			pfree(fc_stringchars);
			pfree(fc_stringmblen);
			pfree(fc_setchars);
			pfree(fc_setmblen);
		}
		else
		{
			/*
			 * 在单字节编码的情况下，我们不需要这样的开销。
			 */
			if (fc_doltrim)
			{
				while (fc_stringlen > 0)
				{
					char		fc_str_ch = *fc_string;

					for (fc_i = 0; fc_i < fc_setlen; fc_i++)
					{
						if (fc_str_ch == fc_set[fc_i])
							break;
					}
					if (fc_i >= fc_setlen)
						break;	/* 此处无匹配 */
					fc_string++;
					fc_stringlen--;
				}
			}

			if (fc_dortrim)
			{
				while (fc_stringlen > 0)
				{
					char		fc_str_ch = fc_string[fc_stringlen - 1];

					for (fc_i = 0; fc_i < fc_setlen; fc_i++)
					{
						if (fc_str_ch == fc_set[fc_i])
							break;
					}
					if (fc_i >= fc_setlen)
						break;	/* 此处无匹配 */
					fc_stringlen--;
				}
			}
		}
	}

	/* 返回字符串的选定部分 */
	return cstring_to_text_with_len(fc_string, fc_stringlen);
}

/*
 * bytea版本的btrim、ltrim、rtrim的通用实现
 */
bytea * dobyteatrim(bytea *string, bytea *set, bool doltrim, bool dortrim)
{
	bytea	   *ret;
	char	   *ptr,
			   *end,
			   *ptr2,
			   *ptr2start,
			   *end2;
	int			m,
				stringlen,
				setlen;

	stringlen = VARSIZE_ANY_EXHDR(string);
	setlen = VARSIZE_ANY_EXHDR(set);

	if (stringlen <= 0 || setlen <= 0)
		return string;

	m = stringlen;
	ptr = VARDATA_ANY(string);
	end = ptr + stringlen - 1;
	ptr2start = VARDATA_ANY(set);
	end2 = ptr2start + setlen - 1;

	if (doltrim)
	{
		while (m > 0)
		{
			ptr2 = ptr2start;
			while (ptr2 <= end2)
			{
				if (*ptr == *ptr2)
					break;
				++ptr2;
			}
			if (ptr2 > end2)
				break;
			ptr++;
			m--;
		}
	}

	if (dortrim)
	{
		while (m > 0)
		{
			ptr2 = ptr2start;
			while (ptr2 <= end2)
			{
				if (*end == *ptr2)
					break;
				++ptr2;
			}
			if (ptr2 > end2)
				break;
			end--;
			m--;
		}
	}

	ret = (bytea *) palloc(VARHDRSZ + m);
	SET_VARSIZE(ret, VARHDRSZ + m);
	memcpy(VARDATA(ret), ptr, m);
	return ret;
}

/********************************************************************
 *
 * byteatrim
 *
 * 语法：
 *
 *	 bytea byteatrim(bytea string, bytea set)
 *
 * 目的：
 *
 *	 返回从前面和后面去掉字符的字符串，直到遇到第一个不在集合中的字符。
 *
 * 从btrim克隆并根据需要修改。
 ********************************************************************/

Datum byteatrim(PG_FUNCTION_ARGS)
{
	bytea	   *fc_string = PG_GETARG_BYTEA_PP(0);
	bytea	   *fc_set = PG_GETARG_BYTEA_PP(1);
	bytea	   *fc_ret;

	fc_ret = dobyteatrim(fc_string, fc_set, true, true);

	PG_RETURN_BYTEA_P(fc_ret);
}

/********************************************************************
 *
 * bytealtrim
 *
 * 语法：
 *
 *	 bytea bytealtrim(bytea string, bytea set)
 *
 * 目的：
 *
 *	 返回去掉初始字符的字符串，直到遇到第一个不在集合中的字符。
 *
 ********************************************************************/

Datum bytealtrim(PG_FUNCTION_ARGS)
{
	bytea	   *fc_string = PG_GETARG_BYTEA_PP(0);
	bytea	   *fc_set = PG_GETARG_BYTEA_PP(1);
	bytea	   *fc_ret;

	fc_ret = dobyteatrim(fc_string, fc_set, true, false);

	PG_RETURN_BYTEA_P(fc_ret);
}

/********************************************************************
 *
 * byteartrim
 *
 * 语法：
 *
 *	 bytea byteartrim(bytea string, bytea set)
 *
 * 目的：
 *
 *	 返回去掉结束字符的字符串，直到遇到最后一个不在集合中的字符。
 *
 ********************************************************************/

Datum byteartrim(PG_FUNCTION_ARGS)
{
	bytea	   *fc_string = PG_GETARG_BYTEA_PP(0);
	bytea	   *fc_set = PG_GETARG_BYTEA_PP(1);
	bytea	   *fc_ret;

	fc_ret = dobyteatrim(fc_string, fc_set, false, true);

	PG_RETURN_BYTEA_P(fc_ret);
}

/********************************************************************
 *
 * ltrim
 *
 * 语法：
 *
 *	 text ltrim(text string, text set)
 *
 * 目的：
 *
 *	 返回去掉初始字符的字符串，直到遇到第一个不在集合中的字符。
 *
 ********************************************************************/

Datum ltrim(PG_FUNCTION_ARGS)
{
	text	   *fc_string = PG_GETARG_TEXT_PP(0);
	text	   *fc_set = PG_GETARG_TEXT_PP(1);
	text	   *fc_ret;

	fc_ret = fc_dotrim(VARDATA_ANY(fc_string), VARSIZE_ANY_EXHDR(fc_string),
				 VARDATA_ANY(fc_set), VARSIZE_ANY_EXHDR(fc_set),
				 true, false);

	PG_RETURN_TEXT_P(fc_ret);
}

/********************************************************************
 *
 * ltrim1 --- ltrim，设置固定为' '
 *
 ********************************************************************/

Datum ltrim1(PG_FUNCTION_ARGS)
{
	text	   *fc_string = PG_GETARG_TEXT_PP(0);
	text	   *fc_ret;

	fc_ret = fc_dotrim(VARDATA_ANY(fc_string), VARSIZE_ANY_EXHDR(fc_string),
				 " ", 1,
				 true, false);

	PG_RETURN_TEXT_P(fc_ret);
}

/********************************************************************
 *
 * rtrim
 *
 * 语法：
 *
 *	 text rtrim(text string, text set)
 *
 * 目的：
 *
 *	 返回去掉结束字符的字符串，直到遇到最后一个不在集合中的字符。
 *
 ********************************************************************/

Datum rtrim(PG_FUNCTION_ARGS)
{
	text	   *fc_string = PG_GETARG_TEXT_PP(0);
	text	   *fc_set = PG_GETARG_TEXT_PP(1);
	text	   *fc_ret;

	fc_ret = fc_dotrim(VARDATA_ANY(fc_string), VARSIZE_ANY_EXHDR(fc_string),
				 VARDATA_ANY(fc_set), VARSIZE_ANY_EXHDR(fc_set),
				 false, true);

	PG_RETURN_TEXT_P(fc_ret);
}

/********************************************************************
 *
 * rtrim1 --- rtrim，设置固定为' '
 *
 ********************************************************************/

Datum rtrim1(PG_FUNCTION_ARGS)
{
	text	   *fc_string = PG_GETARG_TEXT_PP(0);
	text	   *fc_ret;

	fc_ret = fc_dotrim(VARDATA_ANY(fc_string), VARSIZE_ANY_EXHDR(fc_string),
				 " ", 1,
				 false, true);

	PG_RETURN_TEXT_P(fc_ret);
}


/********************************************************************
 *
 * translate
 *
 * 语法：
 *
 *	 text translate(text string, text from, text to)
 *
 * 目的：
 *
 *	 返回字符串，替换所有来自from中的字符为to中对应的字符。如果from的长度大于to，则
 *	 从from中额外字符的出现将被删除。
 *	 改进者：Edwin Ramirez <ramirez@doc.mssm.edu>。
 *
 ********************************************************************/

Datum translate(PG_FUNCTION_ARGS)
{
	text	   *fc_string = PG_GETARG_TEXT_PP(0);
	text	   *fc_from = PG_GETARG_TEXT_PP(1);
	text	   *fc_to = PG_GETARG_TEXT_PP(2);
	text	   *fc_result;
	char	   *fc_from_ptr,
			   *fc_to_ptr,
			   *fc_to_end;
	char	   *fc_source,
			   *fc_target;
	int			fc_m,
				fc_fromlen,
				fc_tolen,
				fc_retlen,
				fc_i;
	int			fc_bytelen;
	int			fc_len;
	int			fc_source_len;
	int			fc_from_index;

	fc_m = VARSIZE_ANY_EXHDR(fc_string);
	if (fc_m <= 0)
		PG_RETURN_TEXT_P(fc_string);
	fc_source = VARDATA_ANY(fc_string);

	fc_fromlen = VARSIZE_ANY_EXHDR(fc_from);
	fc_from_ptr = VARDATA_ANY(fc_from);
	fc_tolen = VARSIZE_ANY_EXHDR(fc_to);
	fc_to_ptr = VARDATA_ANY(fc_to);
	fc_to_end = fc_to_ptr + fc_tolen;

	/*
	 * 最坏情况下的扩展是用最大长度字符在字符串的每个位置替代单字节字符。
	 */
	if (unlikely(pg_mul_s32_overflow(pg_database_encoding_max_length(), fc_m,
									 &fc_bytelen)) ||
		unlikely(pg_add_s32_overflow(fc_bytelen, VARHDRSZ, &fc_bytelen)) ||
		unlikely(!AllocSizeIsValid(fc_bytelen)))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("requested length too large")));

	fc_result = (text *) palloc(fc_bytelen);

	fc_target = VARDATA(fc_result);
	fc_retlen = 0;

	while (fc_m > 0)
	{
		fc_source_len = pg_mblen(fc_source);
		fc_from_index = 0;

		for (fc_i = 0; fc_i < fc_fromlen; fc_i += fc_len)
		{
			fc_len = pg_mblen(&fc_from_ptr[fc_i]);
			if (fc_len == fc_source_len &&
				memcmp(fc_source, &fc_from_ptr[fc_i], fc_len) == 0)
				break;

			fc_from_index++;
		}
		if (fc_i < fc_fromlen)
		{
			/* 替代，或删除如果没有对应的“to”字符 */
			char	   *fc_p = fc_to_ptr;

			for (fc_i = 0; fc_i < fc_from_index; fc_i++)
			{
				if (fc_p >= fc_to_end)
					break;
				fc_p += pg_mblen(fc_p);
			}
			if (fc_p < fc_to_end)
			{
				fc_len = pg_mblen(fc_p);
				memcpy(fc_target, fc_p, fc_len);
				fc_target += fc_len;
				fc_retlen += fc_len;
			}
		}
		else
		{
			/* 没有匹配，因此复制 */
			memcpy(fc_target, fc_source, fc_source_len);
			fc_target += fc_source_len;
			fc_retlen += fc_source_len;
		}

		fc_source += fc_source_len;
		fc_m -= fc_source_len;
	}

	SET_VARSIZE(fc_result, fc_retlen + VARHDRSZ);

	/*
	 * 如果我们使用多字节编码，函数的结果可能会比需要的大得多，但重新分配并不值得；结果可能不会长时间存在。
	 */

	PG_RETURN_TEXT_P(fc_result);
}


/*************************************************************
 *
 * ascii
 *
 * 语法：
 *
 *	 int ascii(文本字符串)
 *
 * 目的：
 *
 *	 返回字符串中第一个字符的十进制表示。
 *	 如果字符串为空，则返回0。
 *	 如果数据库编码为UTF8，则返回Unicode码点。
 *	 如果数据库编码为其他任何多字节编码，则返回第一个字节的值
 *	 如果它是ASCII字符（范围1..127），否则引发错误。
 *	 对于所有其他编码，我们返回第一个字节的值，
 *	 （范围1..255）。
 *
*************************************************************/

Datum ascii(PG_FUNCTION_ARGS)
{
	text	   *fc_string = PG_GETARG_TEXT_PP(0);
	int			fc_encoding = GetDatabaseEncoding();
	unsigned char *fc_data;

	if (VARSIZE_ANY_EXHDR(fc_string) <= 0)
		PG_RETURN_INT32(0);

	fc_data = (unsigned char *) VARDATA_ANY(fc_string);

	if (fc_encoding == PG_UTF8 && *fc_data > 127)
	{
		/* 返回Unicode的码点 */

		int			fc_result = 0,
					fc_tbytes = 0,
					fc_i;

		if (*fc_data >= 0xF0)
		{
			fc_result = *fc_data & 0x07;
			fc_tbytes = 3;
		}
		else if (*fc_data >= 0xE0)
		{
			fc_result = *fc_data & 0x0F;
			fc_tbytes = 2;
		}
		else
		{
			Assert(*fc_data > 0xC0);
			fc_result = *fc_data & 0x1f;
			fc_tbytes = 1;
		}

		Assert(fc_tbytes > 0);

		for (fc_i = 1; fc_i <= fc_tbytes; fc_i++)
		{
			Assert((fc_data[fc_i] & 0xC0) == 0x80);
			fc_result = (fc_result << 6) + (fc_data[fc_i] & 0x3f);
		}

		PG_RETURN_INT32(fc_result);
	}
	else
	{
		if (pg_encoding_max_length(fc_encoding) > 1 && *fc_data > 127)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("requested character too large")));


		PG_RETURN_INT32((int32) *fc_data);
	}
}

/********************************************************************
 *
 * chr
 *
 * 语法：
 *
 *	 text chr(int val)
 *
 * 目的：
 *
 *	返回与 val 的二进制等价的字符。
 *
 * 对于 UTF8，我们将参数视为一个 Unicode 代码点。
 * 对于其他多字节编码，对于超出严格 ASCII 范围的参数（1..127），我们将抛出错误。
 *
 * 我们必须确保不返回在数据库编码中无效的值，以免这成为无效数据进入数据库的途径。
 *
 ********************************************************************/

Datum chr			(PG_FUNCTION_ARGS)
{
	int32		fc_arg = PG_GETARG_INT32(0);
	uint32		fc_cvalue;
	text	   *fc_result;
	int			fc_encoding = GetDatabaseEncoding();

	/*
	 * 对于没有意义或我们无法在编码中有效表示的参数报错。
	 */
	if (fc_arg < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("character number must be positive")));
	else if (fc_arg == 0)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("null character not permitted")));

	fc_cvalue = fc_arg;

	if (fc_encoding == PG_UTF8 && fc_cvalue > 127)
	{
		/* 对于 Unicode，我们将参数视为代码点 */
		int			fc_bytes;
		unsigned char *fc_wch;

		/*
		 * 我们只允许有效的 Unicode 代码点；根据 RFC3629，该范围止于
		 * U+10FFFF，即使 4 字节的 UTF8 序列可以存储值高达
		 * U+1FFFFF。
		 */
		if (fc_cvalue > 0x0010ffff)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("requested character too large for encoding: %u",
							fc_cvalue)));

		if (fc_cvalue > 0xffff)
			fc_bytes = 4;
		else if (fc_cvalue > 0x07ff)
			fc_bytes = 3;
		else
			fc_bytes = 2;

		fc_result = (text *) palloc(VARHDRSZ + fc_bytes);
		SET_VARSIZE(fc_result, VARHDRSZ + fc_bytes);
		fc_wch = (unsigned char *) VARDATA(fc_result);

		if (fc_bytes == 2)
		{
			fc_wch[0] = 0xC0 | ((fc_cvalue >> 6) & 0x1F);
			fc_wch[1] = 0x80 | (fc_cvalue & 0x3F);
		}
		else if (fc_bytes == 3)
		{
			fc_wch[0] = 0xE0 | ((fc_cvalue >> 12) & 0x0F);
			fc_wch[1] = 0x80 | ((fc_cvalue >> 6) & 0x3F);
			fc_wch[2] = 0x80 | (fc_cvalue & 0x3F);
		}
		else
		{
			fc_wch[0] = 0xF0 | ((fc_cvalue >> 18) & 0x07);
			fc_wch[1] = 0x80 | ((fc_cvalue >> 12) & 0x3F);
			fc_wch[2] = 0x80 | ((fc_cvalue >> 6) & 0x3F);
			fc_wch[3] = 0x80 | (fc_cvalue & 0x3F);
		}

		/*
		 * 之前的范围检查是不够的，因为 UTF8 排除了 Unicode 的“代理对”代码。确保我们创建的内容是有效的
		 * UTF8。
		 */
		if (!pg_utf8_islegal(fc_wch, fc_bytes))
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("requested character not valid for encoding: %u",
							fc_cvalue)));
	}
	else
	{
		bool		fc_is_mb;

		fc_is_mb = pg_encoding_max_length(fc_encoding) > 1;

		if ((fc_is_mb && (fc_cvalue > 127)) || (!fc_is_mb && (fc_cvalue > 255)))
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("requested character too large for encoding: %u",
							fc_cvalue)));

		fc_result = (text *) palloc(VARHDRSZ + 1);
		SET_VARSIZE(fc_result, VARHDRSZ + 1);
		*VARDATA(fc_result) = (char) fc_cvalue;
	}

	PG_RETURN_TEXT_P(fc_result);
}

/********************************************************************
 *
 * repeat
 *
 * 语法：
 *
 *	 text repeat(text string, int val)
 *
 * 目的：
 *
 *	根据 val 重复字符串。
 *
 ********************************************************************/

Datum repeat(PG_FUNCTION_ARGS)
{
	text	   *fc_string = PG_GETARG_TEXT_PP(0);
	int32		fc_count = PG_GETARG_INT32(1);
	text	   *fc_result;
	int			fc_slen,
				fc_tlen;
	int			fc_i;
	char	   *fc_cp,
			   *fc_sp;

	if (fc_count < 0)
		fc_count = 0;

	fc_slen = VARSIZE_ANY_EXHDR(fc_string);

	if (unlikely(pg_mul_s32_overflow(fc_count, fc_slen, &fc_tlen)) ||
		unlikely(pg_add_s32_overflow(fc_tlen, VARHDRSZ, &fc_tlen)) ||
		unlikely(!AllocSizeIsValid(fc_tlen)))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("requested length too large")));

	fc_result = (text *) palloc(fc_tlen);

	SET_VARSIZE(fc_result, fc_tlen);
	fc_cp = VARDATA(fc_result);
	fc_sp = VARDATA_ANY(fc_string);
	for (fc_i = 0; fc_i < fc_count; fc_i++)
	{
		memcpy(fc_cp, fc_sp, fc_slen);
		fc_cp += fc_slen;
		CHECK_FOR_INTERRUPTS();
	}

	PG_RETURN_TEXT_P(fc_result);
}
